﻿using System;
using System.Data;
using System.Threading;
using MuZa.DBConnector;

namespace MuZa.MuZa
{
    internal class AgentI
    {
        private int noTask;
        private DataTable dtPriority;
        private DataTable dtTable;
        private DataTable dtTask;
        private bool jobDone;
        private int noBooking;
        private int noCancellation;
        private int noWaitingMsg;
        private int requestIndex;
        private int noRequest;
        private bool finished = false;

        private State State;

        //public int Id { get; set; }
        public String Name { get; set; }

        public String Location { get; set; }

        public int TotalCost { get; set; }

        public int TotalDurationHours { get; set; }

        public AgentI()
        {
            //number of the sent request messages
            noWaitingMsg = 0;
            jobDone = false;
            //number of the row in the GiveInfo2 list
            this.requestIndex = 0;
            this.noBooking = 0;
            this.noTask = 0;
            this.noCancellation = 0;
            this.noRequest = 0;

            DataTable dtTask = new DataTable();
            DataTable dtPriority = new DataTable();
            DataTable dtTable = new DataTable();
        }

        public AgentI(string name, string location, int totalCost, int totalDurationHours)
        {
            Name = name;
            Location = location;
            TotalCost = totalCost;
            TotalDurationHours = totalDurationHours;
        }

        public AgentI(string name, string location, int totalCost, int totalDurationHours, State state)
        {
            //number of the sent request messages
            noWaitingMsg = 0;
            jobDone = false;
            //number of the row in the GiveInfo2 list
            this.requestIndex = 0;
            this.noBooking = 0;
            this.noTask = 0;
            this.noCancellation = 0;
            this.noRequest = 0;

            dtTask = new DataTable();
            dtPriority = new DataTable();
            dtTable = new DataTable();

            Name = name;
            Location = location;
            TotalCost = totalCost;
            TotalDurationHours = totalDurationHours;
            State = state;
        }

        public void Run()
        {
            //1. Get initiator id.
            int initId = GetData.GetInitiatorId(Name);

            //2. Get Task list.
            this.dtTask = GetData.GetAllTasksFromInitiator(initId);

            //3. Get Priority list
            this.dtPriority = GetData.GetAllPriorityFromInitiator(initId);

            // ask info board
            AskInfoBoard();
            while (true) // no of tasks shoul be taken from the task table
            {
                if (this.finished == true)
                {
                    Thread.Sleep(5000);
                    //Console.WriteLine("Finished job by " + this.Name);
                }
                else
                {
                    if (State.Free)
                    //if (true)
                    {
                        lock (this)
                        {
                            State.Free = false;
                            Console.WriteLine("Entering " + this.Name);
                            //Steps:

                            // Rules:
                            // 1. Index will be increased before asking

                            // if this is the first time
                            if (this.noWaitingMsg == 0)
                            {
                                this.requestIndex += 1;
                                SendResourceRequest();
                                this.noCancellation = 0;
                                this.noWaitingMsg = 3;
                                this.noBooking = 0;
                            }
                            // if agent is waiting for replies
                            else if (this.noWaitingMsg > 0)
                            {
                                CheckRequestMessage();
                            }
                            // if request index != 0 & waiting msg = 0 & cancel=0 & booking =0 then send res request with new index
                            else
                            {
                                Console.WriteLine("Unhandled operation from " + this.Name);
                                Console.WriteLine("Request Index " + this.requestIndex + " Waiting Msg " + this.noWaitingMsg);
                            }

                            // 5 sec
                            Console.WriteLine("Exiting " + this.Name);
                            State.Free = true;
                            Thread.Sleep(5000);
                        }
                    }
                    else
                    {
                        Thread.Sleep(10);
                    }
                }
            }
        }

        private void CheckRequestMessage()
        {
            DataTable dtMessages = GetData.GetMessagesForInitiator(Name);
            int noOfTotalTasks = this.dtTask.Rows.Count;

            foreach (DataRow msgRow in dtMessages.Rows)
            {
                // Reading message details
                string resName = msgRow.ItemArray[2].ToString();
                int reseQuantity = Convert.ToInt32(msgRow.ItemArray[3]);
                string resType = msgRow.ItemArray[7].ToString();

                int msgId = Convert.ToInt32(msgRow.ItemArray[5]);
                int msgStatus = Convert.ToInt32(msgRow.ItemArray[4]);

                int deadLine = Convert.ToInt32(msgRow.ItemArray[8]);

                // Taking actions according to the messages
                if (msgStatus == MessageStatus.UnreadMessage)
                {
                    // if(Deadline is Over then put refused as status)
                    if (!GetData.IsValidDeadline(deadLine))
                    {
                        GetData.UpdateMessageStatus(msgId, MessageStatus.RefusedByParticipant);
                        //this.noCancellation += 1;
                        //this.noWaitingMsg -= 1;
                    }
                }
                else if (msgStatus == MessageStatus.RefusedByParticipant)
                {
                    GetData.UpdateMessageStatus(msgId, MessageStatus.TrashMessage);
                    this.noCancellation += 1;
                    //this.noWaitingMsg -= 1;
                }
                else if (msgStatus == MessageStatus.BookedByParticipant)
                {
                    GetData.UpdateMessageStatus(msgId, MessageStatus.CheckedBookedByParticipant);
                    this.noBooking += 1;
                }
                else if (msgStatus == MessageStatus.FinishTaskByParticipant)
                {
                    GetData.UpdateMessageStatus(msgId, MessageStatus.CheckedFinishTaskByInitiator);
                    InfoBoard.IncrementNoFTbyInitiator(Convert.ToInt32(msgRow.ItemArray[0]));
                    GetData.WriteLog(this.Name, "Information Board", "Update", "NoFT", "phase_6", this.Name + "updates NoFT to IB");
                }
                else if (msgStatus == MessageStatus.SuccessfulyFinished)
                {
                    this.noTask += 1;
                    if (this.noTask == 3)
                    {
                        this.finished = true;
                        GetData.numInit += 1;
                    }
                }
                else
                {
                }

                // if the number of cancellation is 1 and booked 2 then replan
                if (this.noCancellation == 1 && this.noBooking == 2)
                {
                    Replanning();
                    this.noWaitingMsg = 3;
                    this.noCancellation = 0;
                }
                // if the number of cancellation is 2 and booked 1 then cancel all booking and go to the next index
                else if (this.noCancellation == 2 && this.noBooking == 1)
                {
                    // cancel all booking and go to next index
                    CancelAllBookings();
                    //this.requestIndex += 1;
                    this.noWaitingMsg = 0;
                    this.noCancellation = 0;
                    this.noBooking = 0;
                    // go to next index
                    this.requestIndex += 1;
                    AskToNextIndexOfTable();
                }
                // if the number of cancellation is 3 and booked 0 then go to next index
                else if (this.noCancellation == 3 && this.noBooking == 0)
                {
                    // cancel all booking and go to next index
                    CancelAllBookings();
                    //this.requestIndex += 1;
                    this.noWaitingMsg = 0;
                    this.noCancellation = 0;
                    // go to next index
                    this.requestIndex += 1;
                    AskToNextIndexOfTable();
                }
                else if (this.noCancellation == 0 && this.noBooking == 3)
                {
                    MakeContract();
                }
                else
                {
                }
            }
        }

        private void MakeContract()
        {
            DataTable bookedMsgTable = new DataTable();
            bookedMsgTable = GetData.GetBookedParticipantInfo(this.Name);

            if (bookedMsgTable.Rows.Count != 0)
                for (int j = 0; j < noBooking; j++)
                {
                    int p_id = Convert.ToInt32(bookedMsgTable.Rows[j][0]);
                    string resource_name = bookedMsgTable.Rows[j][2].ToString();
                    string resource_type = bookedMsgTable.Rows[j][7].ToString();
                    int resource_quantity = Convert.ToInt32(bookedMsgTable.Rows[j][3]);
                    // Cancel the booked resources
                    GetData.UpdateMessageStatus(Convert.ToInt32(bookedMsgTable.Rows[j][5]), MessageStatus.AgreedByInitiator);
                    GetData.WriteLog(this.Name, GetData.GetParticipantName(p_id), "contract agreed", "contract", "phase_4", "agreed contract for " + resource_type + " " + resource_name + " " + resource_quantity);
                    InfoBoard.IncrementNoTbyInitiator(Convert.ToInt32(bookedMsgTable.Rows[j][0]));
                    GetData.WriteLog(this.Name, "Information Board", "Update", "NoT", "phase_4", this.Name + "updates NoT to IB");
                }
        }

        private void AskToNextIndexOfTable()
        {
            // check if the index is out of range
            if (this.requestIndex < this.dtTable.Rows.Count)
            {
                SendResourceRequest();
                this.noWaitingMsg = 3;
                this.noCancellation = 0;
            }
            else
            {
                // ask info board
                AskInfoBoard();
            }
        }

        private void AskInfoBoard()
        {
            GetData.WriteLog(this.Name, "Information Board", "Request", "Participant List", "phase_2", "asking for participant list for " + this.Name);
            this.dtTable = InfoBoard.GiveInfo2(dtTask, dtPriority, Location, TotalCost, TotalDurationHours);
            GetData.WriteLog("Information Board", this.Name, "Reply", "Participant List", "phase_2", "giving " + dtTable.Rows.Count + " set of participant list to " + this.Name);
            Console.WriteLine("Asking IB returned " + dtTable.Rows.Count + " rows.");

            this.requestIndex = 0;
            this.noWaitingMsg = 0;
            this.noBooking = 0;

            // the table is not empty
            if (this.dtTable.Rows.Count > 0)
            {
                SendResourceRequest();
                //this.requestIndex += 1;
                this.noWaitingMsg = 3;
                //this.noRequest = 3;
            }
            // the table is empty and no use of asking anymore
            else
            {
                CancelAllBookings();
                this.finished = true;
                GetData.numInit += 1;
            }
        }

        private void Replanning()
        {
            DataTable bookedMsgTable = new DataTable();
            bookedMsgTable = GetData.GetBookedParticipantInfo(Name);

            int p_id_1 = Convert.ToInt32(bookedMsgTable.Rows[0][0]);
            int deadLine_1 = Convert.ToInt32(bookedMsgTable.Rows[0][8]);
            string resource_name_1 = bookedMsgTable.Rows[0][2].ToString();
            string resource_type_1 = bookedMsgTable.Rows[0][7].ToString();
            string start_location_1 = bookedMsgTable.Rows[0][9].ToString();
            string end_location_1 = bookedMsgTable.Rows[0][10].ToString();
            int resource_quantity_1 = Convert.ToInt32(bookedMsgTable.Rows[0][3]);

            int p_id_2 = Convert.ToInt32(bookedMsgTable.Rows[1][0]);
            int deadLine_2 = Convert.ToInt32(bookedMsgTable.Rows[1][8]);
            string resource_name_2 = bookedMsgTable.Rows[1][2].ToString();
            string resource_type_2 = bookedMsgTable.Rows[1][7].ToString();
            string start_location_2 = bookedMsgTable.Rows[1][9].ToString();
            string end_location_2 = bookedMsgTable.Rows[1][10].ToString();
            int resource_quantity_2 = Convert.ToInt32(bookedMsgTable.Rows[1][3]);

            //look to the list for the pair which contains booking participant id
            int asking_task_index = 0;
            int new_p_id = SearchPairForParticipant(p_id_1, resource_name_1, resource_type_1, start_location_1, end_location_1, p_id_2, resource_name_2, resource_type_2, start_location_2, end_location_2,
                                                    ref asking_task_index);

            // found valid participant, then send the resource request
            if (new_p_id != -1)
            {
                //send request to him with the deadline less than booking deadline (2 sec)
                int task_quantity = Convert.ToInt32(dtTask.Rows[asking_task_index].ItemArray[2]);
                //string new_resource_name = dtTask.Rows[asking_task_index].ItemArray[1].ToString();
                string new_resource_name = dtTable.Rows[requestIndex].ItemArray[asking_task_index * 11 + 4].ToString();
                string new_resource_type = dtTask.Rows[asking_task_index].ItemArray[3].ToString();
                string new_start_location = dtTask.Rows[asking_task_index].ItemArray[5].ToString();
                string new_end_location = dtTable.Rows[requestIndex].ItemArray[32].ToString();

                int deadLine = 0;
                if (deadLine_1 < deadLine_2)
                    deadLine = deadLine_1;
                else
                    deadLine = deadLine_2;
                deadLine -= 2;

                SendSingleResourceRequest(new_p_id, new_resource_name, new_resource_type, task_quantity, new_start_location, new_end_location,
                                            deadLine);

                //this.noWaitingMsg += 1;
            }
            // no participant found, then ask the IB again
            else
            {
                AskInfoBoard();
            }
        }

        public void SendResourceRequest()
        {
            // Add a deadline for response
            //int dt = DateTime.Now;
            var RandNum = new Random();
            //dt = dt.AddSeconds(RandNum.Next(1, 5));
            int dt = GetData.sw.Elapsed.Seconds;
            dt = dt += 30;

            // Send request for task 1
            int p_id_1 = Convert.ToInt32(dtTable.Rows[this.requestIndex].ItemArray[0]);
            string i_name = Name;
            string res_name_1 = dtTable.Rows[this.requestIndex].ItemArray[4].ToString();
            string res_type_1 = dtTable.Rows[this.requestIndex].ItemArray[8].ToString();
            int tsk_quantity_1 = Convert.ToInt32(dtTask.Rows[0].ItemArray[2]);
            string start_location_1 = dtTable.Rows[this.requestIndex].ItemArray[9].ToString();
            string end_location_1 = dtTable.Rows[this.requestIndex].ItemArray[10].ToString();

            GetData.SendResourceRequest(p_id_1, i_name, res_name_1, res_type_1, tsk_quantity_1, start_location_1, end_location_1, dt);
            GetData.IncrementNoRbyInitiator(p_id_1);
            GetData.WriteLog(this.Name, GetData.GetParticipantName(p_id_1), "resource request", "resource", "phase_3", "request for " + res_type_1 + " " + res_name_1 + " " + tsk_quantity_1);
            GetData.WriteLog(this.Name, "Information Board", "Update", "NoR","phase_3", this.Name + "updates NoR to IB");

            // Send request for task 2
            int p_id_2 = Convert.ToInt32(dtTable.Rows[this.requestIndex].ItemArray[11]);
            string res_name_2 = dtTable.Rows[this.requestIndex].ItemArray[15].ToString();
            string res_type_2 = dtTable.Rows[this.requestIndex].ItemArray[19].ToString();
            int tsk_quantity_2 = Convert.ToInt32(dtTask.Rows[1].ItemArray[2]);
            string start_location_2 = dtTable.Rows[this.requestIndex].ItemArray[20].ToString();
            string end_location_2 = dtTable.Rows[this.requestIndex].ItemArray[21].ToString();

            GetData.SendResourceRequest(p_id_2, i_name, res_name_2, res_type_2, tsk_quantity_2, start_location_2, end_location_2, dt);
            GetData.IncrementNoRbyInitiator(p_id_2);
            GetData.WriteLog(this.Name, GetData.GetParticipantName(p_id_2), "resource request", "resource", "phase_3", "request for " + res_type_2 + " " + res_name_2 + " " + tsk_quantity_2);
            GetData.WriteLog(this.Name, "Information Board", "Update", "NoR", "phase_3", this.Name + "updates NoR to IB");

            // Send request for task 3
            int p_id_3 = Convert.ToInt32(dtTable.Rows[this.requestIndex].ItemArray[22]);
            string res_name_3 = dtTable.Rows[this.requestIndex].ItemArray[26].ToString();
            string res_type_3 = dtTable.Rows[this.requestIndex].ItemArray[30].ToString();
            int tsk_quantity_3 = Convert.ToInt32(dtTask.Rows[2].ItemArray[2]);
            string start_location_3 = dtTable.Rows[this.requestIndex].ItemArray[31].ToString();
            string end_location_3 = dtTable.Rows[this.requestIndex].ItemArray[32].ToString();

            GetData.SendResourceRequest(p_id_3, i_name, res_name_3, res_type_3, tsk_quantity_3, start_location_3, end_location_3, dt);
            GetData.IncrementNoRbyInitiator(p_id_3);
            GetData.WriteLog(this.Name, GetData.GetParticipantName(p_id_3), "resource request", "resource", "phase_3", "request for " + res_type_3 + " " + res_name_3 + " " + tsk_quantity_3);
            GetData.WriteLog(this.Name, "Information Board", "Update", "NoR", "phase_3", this.Name + "updates NoR to IB");
        }

        public void SendSingleResourceRequest(int p_id, string res_name, string res_type, int tsk_quantity, string start_loc, string end_loc, int dt)
        {
            GetData.SendResourceRequest(p_id, Name, res_name, res_type, tsk_quantity, start_loc, end_loc, dt);
            GetData.IncrementNoRbyInitiator(p_id);
            GetData.WriteLog(this.Name, GetData.GetParticipantName(p_id), "resource request", "resource", "phase_3", "request for " + res_type + " " + res_name + " " + tsk_quantity);
            GetData.WriteLog(this.Name, "Information Board", "Update", "NoR", "phase_3", this.Name + "updates NoR to IB");
        }

        public int SearchPairForParticipant(int p_id_1, string resource_name_1, string resource_type_1, string start_location_1, string end_location_1, int p_id_2, string resource_name_2, string resource_type_2, string start_location_2, string end_location_2, ref int task_index)
        {
            int new_p_id = -1;
            for (int i = requestIndex + 1; i < dtTable.Rows.Count; i++)
            {
                if (((Convert.ToInt32(dtTable.Rows[i].ItemArray[0]) == p_id_1 &&
                    dtTable.Rows[i].ItemArray[4].ToString() == resource_name_1 &&
                    dtTable.Rows[i].ItemArray[8].ToString() == resource_type_1 &&
                    dtTable.Rows[i].ItemArray[9].ToString() == start_location_1 &&
                    dtTable.Rows[i].ItemArray[10].ToString() == end_location_1) &&
                    (Convert.ToInt32(dtTable.Rows[i].ItemArray[11]) == p_id_2 &&
                    dtTable.Rows[i].ItemArray[15].ToString() == resource_name_2 &&
                    dtTable.Rows[i].ItemArray[19].ToString() == resource_type_2 &&
                    dtTable.Rows[i].ItemArray[20].ToString() == start_location_2 &&
                    dtTable.Rows[i].ItemArray[21].ToString() == end_location_2))
                    ||
                    ((Convert.ToInt32(dtTable.Rows[i].ItemArray[0]) == p_id_2 &&
                    dtTable.Rows[i].ItemArray[4].ToString() == resource_name_2 &&
                    dtTable.Rows[i].ItemArray[8].ToString() == resource_type_2 &&
                    dtTable.Rows[i].ItemArray[9].ToString() == start_location_2 &&
                    dtTable.Rows[i].ItemArray[10].ToString() == end_location_2) &&
                    (Convert.ToInt32(dtTable.Rows[i].ItemArray[11]) == p_id_1 &&
                    dtTable.Rows[i].ItemArray[15].ToString() == resource_name_1 &&
                    dtTable.Rows[i].ItemArray[19].ToString() == resource_type_1 &&
                    dtTable.Rows[i].ItemArray[20].ToString() == start_location_1 &&
                    dtTable.Rows[i].ItemArray[21].ToString() == end_location_1)))
                {
                    new_p_id = Convert.ToInt32(dtTable.Rows[i].ItemArray[22]);
                    this.requestIndex = i;
                    task_index = 2;
                    break;
                }
                if (((Convert.ToInt32(dtTable.Rows[i].ItemArray[0]) == p_id_1 &&
                    dtTable.Rows[i].ItemArray[4].ToString() == resource_name_1 &&
                    dtTable.Rows[i].ItemArray[8].ToString() == resource_type_1 &&
                    dtTable.Rows[i].ItemArray[9].ToString() == start_location_1 &&
                    dtTable.Rows[i].ItemArray[10].ToString() == end_location_1) &&
                    (Convert.ToInt32(dtTable.Rows[i].ItemArray[22]) == p_id_2 &&
                    dtTable.Rows[i].ItemArray[26].ToString() == resource_name_2 &&
                    dtTable.Rows[i].ItemArray[30].ToString() == resource_type_2 &&
                    dtTable.Rows[i].ItemArray[31].ToString() == start_location_2 &&
                    dtTable.Rows[i].ItemArray[32].ToString() == end_location_2))
                    ||
                    ((Convert.ToInt32(dtTable.Rows[i].ItemArray[0]) == p_id_2 &&
                    dtTable.Rows[i].ItemArray[4].ToString() == resource_name_2 &&
                    dtTable.Rows[i].ItemArray[8].ToString() == resource_type_2 &&
                    dtTable.Rows[i].ItemArray[9].ToString() == start_location_2 &&
                    dtTable.Rows[i].ItemArray[10].ToString() == end_location_2) &&
                    (Convert.ToInt32(dtTable.Rows[i].ItemArray[22]) == p_id_1 &&
                    dtTable.Rows[i].ItemArray[26].ToString() == resource_name_1 &&
                    dtTable.Rows[i].ItemArray[30].ToString() == resource_type_1 &&
                    dtTable.Rows[i].ItemArray[31].ToString() == start_location_1 &&
                    dtTable.Rows[i].ItemArray[32].ToString() == end_location_1)))
                {
                    new_p_id = Convert.ToInt32(dtTable.Rows[i].ItemArray[11]);
                    this.requestIndex = i;
                    task_index = 1;
                    break;
                }
                if (((Convert.ToInt32(dtTable.Rows[i].ItemArray[11]) == p_id_1 &&
                    dtTable.Rows[i].ItemArray[15].ToString() == resource_name_1 &&
                    dtTable.Rows[i].ItemArray[19].ToString() == resource_type_1 &&
                    dtTable.Rows[i].ItemArray[20].ToString() == start_location_1 &&
                    dtTable.Rows[i].ItemArray[21].ToString() == end_location_1) &&
                    (Convert.ToInt32(dtTable.Rows[i].ItemArray[22]) == p_id_2 &&
                    dtTable.Rows[i].ItemArray[26].ToString() == resource_name_2 &&
                    dtTable.Rows[i].ItemArray[30].ToString() == resource_type_2 &&
                    dtTable.Rows[i].ItemArray[31].ToString() == start_location_2 &&
                    dtTable.Rows[i].ItemArray[32].ToString() == end_location_2))
                    ||
                    ((Convert.ToInt32(dtTable.Rows[i].ItemArray[11]) == p_id_2 &&
                    dtTable.Rows[i].ItemArray[15].ToString() == resource_name_2 &&
                    dtTable.Rows[i].ItemArray[19].ToString() == resource_type_2 &&
                    dtTable.Rows[i].ItemArray[20].ToString() == start_location_2 &&
                    dtTable.Rows[i].ItemArray[21].ToString() == end_location_2) &&
                    (Convert.ToInt32(dtTable.Rows[i].ItemArray[22]) == p_id_1 &&
                    dtTable.Rows[i].ItemArray[26].ToString() == resource_name_1 &&
                    dtTable.Rows[i].ItemArray[30].ToString() == resource_type_1 &&
                    dtTable.Rows[i].ItemArray[31].ToString() == start_location_1 &&
                    dtTable.Rows[i].ItemArray[32].ToString() == end_location_1)))
                {
                    new_p_id = Convert.ToInt32(dtTable.Rows[i].ItemArray[0]);
                    this.requestIndex = i;
                    task_index = 0;
                    break;
                }
            }

            return new_p_id;
        }

        public bool CancelAllBookings()
        {
            DataTable bookedMsgTable = new DataTable();
            bookedMsgTable = GetData.GetBookedParticipantInfo(true, this.Name);

            if (bookedMsgTable.Rows.Count > 0)
                for (int j = 0; j < bookedMsgTable.Rows.Count; j++)
                {
                    int p_id = Convert.ToInt32(bookedMsgTable.Rows[j][0]);
                    string resource_name = bookedMsgTable.Rows[j][2].ToString();
                    string resource_type = bookedMsgTable.Rows[j][7].ToString();
                    int resource_quantity = Convert.ToInt32(bookedMsgTable.Rows[j][3]);
                    // Cancel the booked resources
                    GetData.UpdateMessageStatus(Convert.ToInt32(bookedMsgTable.Rows[j][5]), MessageStatus.RefusedByInitiator);
                    GetData.WriteLog(this.Name, GetData.GetParticipantName(p_id), "refuse request", "booking", "phase_3", "cancel booking for " + resource_type + " " + resource_name + " " + resource_quantity);
                }
            return true;
        }
    }
}